

; Physics Base Class
; handles physical properties like position, velocity
; characteristics vars are read by world


(load "optimization.scm")
(load "globals.scm")
(load "vector.scm")
(load "utility.scm")
(require scheme)


(define physics-object% 
  (class object%
    
    ;PUBLIC VARIABLES
    (init-field [position     (make-vector 2 #i0)]      ; physics vars
                [velocity     (make-vector 2 #i0)]
                [collision-occured #f]                  ; processing vars (per frame)
                [collision-list    '()]
                [reaction          (make-vector 2 #i0)]
                [size                #f]                ; characteristics vars
                [collision-mask      #f]
                [affected-by-gravity #t]
                [interactive         #t]         
                [solid               #t]
                [bounce              #t])
    
    ; GETTERS
    (define/public (get-position)          position)
    (define/public (get-velocity)          velocity)
    (define/public (get-collision-occured) collision-occured)
    (define/public (get-collision-list)    collision-list)
    (define/public (get-reaction)          reaction)
    (define/public (get-size)              size)
    
    ; SETTERS
    (define/public (set-position          value) (set! position          value))
    (define/public (set-velocity          value) (set! velocity          value))
    (define/public (set-collision-occured value) (set! collision-occured value))
    (define/public (set-collision-list    value) (set! collision-list    value))
    (define/public (set-reaction          value) (set! reaction          value))
    (define/public (set-size              value) (set! size              value))
    
    ; QUERIES
    (define/public (affected-by-gravity?) affected-by-gravity)
    (define/public (bounce?)              bounce)
    (define/public (solid?)               solid)
    (define/public (interactive?)         interactive)
    
    
    ; Update
    (define/public (update)
      (void))
    
    ; Collide
    (define/public (collide x y collision)
      (void))
    
    ; Draw
    (define/public (draw dc)
      (void))
    
    ; Add Collision
    (define/public (add-collision collision)
      (unless (member collision collision-list)
        (set! collision-list (cons collision collision-list))))
    
    ; Generate Ellipse Collision Mask
    (define/public (generate-ellipse-collision-mask)
      (when size
        (set! collision-mask (make-vector (* (get-x size) (get-y size)) #t))
        (let ([x-radius (fl/ (get-x size) #i2)]
              [y-radius (fl/ (get-y size) #i2)])
          (for ([x (in-range (get-x size))])
            (for ([y (in-range (get-y size))])
              (when (fl> (fl+ (expt (fl/ (fl- (fl+ x #i1/2) x-radius) x-radius) #i2) (expt (fl/ (fl- (fl+ y #i1/2) y-radius) y-radius) #i2)) #i1)
                (vector-set! collision-mask (point->index x y (get-x size)) #f)))))))
    
    ; For Physics Space
    (define/public (for-physics-space calc-position op)
      (when size
        (let ([pos-x (inexact->exact (truncate (get-x calc-position)))]
              [pos-y (inexact->exact (truncate (get-y calc-position)))]
              [center-x (fxquotient (get-x size) 2)]
              [center-y (fxquotient (get-y size) 2)]
              [width (get-x size)])
          (for ([x (in-range (get-x size))])
            (for ([y (in-range (get-y size))])
              (when (or (not collision-mask) (vector-ref collision-mask (point->index x y width)))
                (op (- x center-x) (- y center-y) pos-x pos-y)))))))
    
    ; Randomize Position
    (define/public (randomize-position)
      (let ([stuck #f])
        (let randomize-position-loop ()
          (set! position (vector (fixnum->flonum (random (send *world* get-width))) (fixnum->flonum (random (send *world* get-height)))))
          (set! stuck #f)
          (for-physics-space position
                             (lambda (x y pos-x pos-y)
                               (unless (eq? 'empty (send *world* get-collision (+ x pos-x) (+ y pos-y)))
                                 (set! stuck #t))))
          (when stuck
            (randomize-position-loop)))))
    
    ; Constructor/Initialization
    (super-new)
    (unless collision-mask
      (generate-ellipse-collision-mask))))

